#ifndef CodeEdit_H
#define CodeEdit_H

#include <QPlainTextEdit>
#include <QCompleter>

inline QString end_of_word("~!@#$%^&*()+{}|:\"<>?,./;'[]\\-=");
inline QString not_a_word("~!@#$%^&*()+{}|:\"<>?,./;'[]\\-= \t");
inline QString not_front_of_word("~!@#$%^&*()+{}|:\"<>?,./;'[]\\-= \t0123456789");

class GeneralTextViewAdaptor;
class Highlighter;
class Minimap;

/// @todo Сделать подробное описание параметров, удобное для добавления в GUI настройки 

struct CodeEditParameters
{
    int     tab_size                    = 4;
    QString font_name                   = "";
    std::vector<QString> font_names;
    int     font_zero_size              = 10;
    int     font_default_size           = 12;
    bool    wrap_lines                  = true;
    bool    line_number_area            = true;
    bool    minimap                     = true;
    int     minimap_font_point_size     = 1;
    bool    minimap_auto_scaling        = true;

    bool    auto_indent                 = true;
    bool    comment_by_slash            = true;
    QString line_comment_start_chars    = "";
    QString comment_begin_chars         = "";
    QString comment_finish_chars        = "";
    bool    tabs_replacing              = true;
    /// @todo Сделать отсечение невидимых символов (пробелы и табуляции) в конце 
    /// строк при сохранении
    // bool    truncate_spaces_in_lines    = true;

    bool    use_completer               = true;
    int     chars_count_for_completer_popup = 3;
    bool    use_lexis_for_completer     = true;
    bool    lsp_completer               = true;
    bool    ignore_right_side_of_word   = true;
    int     completer_max_rows          = 10;
    int     cursor_position_change_delay_mills = 500;

    bool    syntax_highlight            = true;
    QString highlight_data_file         = "default.json";
    bool    lsp_support                 = true;
    bool    lsp_hover                   = true;
    bool    document_symbols            = true;
    bool    semantic_tokens             = true;
    bool    semantic_postorder          = false;
    bool    hover_for_identifiers_only  = true;

    int     lsp_change_delay_mills      = 1500;

    bool    autosave                    = false;
    int     autosave_change_delay_mills = 2000;
    bool    save_backup_file            = false;
};

class CodeEdit : public QPlainTextEdit
{
    friend class LineNumberArea;
    friend class Highlighter;

    Q_OBJECT

    CodeEditParameters  _params;

    QWidget *           _line_number_area = nullptr;
    Highlighter *       _highlighter      = nullptr;
    Minimap *           _minimap          = nullptr;

    QCompleter *        _completer = nullptr;

    QPoint              _tooltip_pos;
    QString             _tooltip_text;
    QString             _last_hover_word;
    int                 _last_hover_word_expired_timer;

    QAction *           _text_block_shift_right_action;
    QAction *           _text_block_shift_left_action;
    QAction *           _text_handle_comment_action;

    QAction *           _save_screen_to_image_action;
    QAction *           _save_text_to_image_action;

    QVector<QString>    _trigger_characters;

    int                 _clicked_line;
    int                 _clicked_character;

    int                 _reference_line = -1;
    int                 _reference_character = -1;
    int                 _reference_length = 0;

    int                 _mm_position, 
                        _mm_chars_removed = 0, 
                        _mm_chars_added = 0;

    int                 _last_line_number_area_width = 0, 
                        _last_minimap_width = 0;

    QTextDocument::FindFlags _find_options = QTextDocument::FindFlags();

public:
    CodeEdit(CodeEditParameters params);
    ~CodeEdit();

    const CodeEditParameters &  params() const { return _params; } 
    void                        setParams(CodeEditParameters params) { _params = params; }

    const QVector<QString> &    trigger_characters() const { return _trigger_characters; }

    int reference_line() const { return _reference_line; }
    int reference_character() const { return _reference_character; }
    int reference_length() const { return _reference_length; }

    int calcLineNumberAreaWidth();
    int calcMinimapWidth(int line_number_area_width);

    void resetHighlighter(Highlighter * highlighter);
    void rehighlight();
    Highlighter * highlighter() { return _highlighter; }

    bool resetCompleter(QAbstractItemModel *model, 
                        const std::vector<std::u16string> & trigger_characters,
                        QAbstractItemView * popup=nullptr);
    QCompleter * completer() { return _completer; }
    QTextCursor selectWord() const;
    QTextCursor selectLeftPunctuation() const;
    void    showCompleterPopup();

    void resetMinimap();
    void resetFont(QFont font);

    void setLineCol(QPair<int,int> line_col);

    QString getErrorText(int line, int character=-1);

    void hover(QString value);

    QPixmap toPixmap();
    QPixmap getScreenshot();

    QPair<int,int> getViewportLines();
    void nearToClose();

    void setFindOptions(QTextDocument::FindFlags find_options) { _find_options = find_options; }

signals:
    void gotFocus();
    void lostFocus();
    void requestHover(int line, int character);
    void requestDeclaration(int line, int character);
    void requestDefinition(int line, int character);
    void requestForceCompletion();
    void requestScreenshot();
    void requestSaveAsPicture();

protected:
    virtual void resizeEvent(QResizeEvent * event) override;
    virtual void focusInEvent(QFocusEvent * event) override;
    virtual void focusOutEvent(QFocusEvent * event) override;
    virtual void wheelEvent(QWheelEvent * event) override;
    virtual bool event(QEvent * event) override;
    virtual void timerEvent(QTimerEvent *event) override;
    virtual void paintEvent(QPaintEvent * event) override;
    virtual void contextMenuEvent(QContextMenuEvent *e) override;
    virtual void keyPressEvent(QKeyEvent *e) override;
    virtual void mousePressEvent(QMouseEvent *event) override;
    virtual void mouseReleaseEvent(QMouseEvent *event) override;
    virtual void mouseMoveEvent(QMouseEvent *event) override;
    virtual void scrollContentsBy(int dx, int dy) override;

protected:
    enum class ShiftDirection
    {
        Left,
        Right
    };

    int     shiftRight(QTextCursor & cursor, int end_selection);
    int     shiftLeft(QTextCursor & cursor, int end_selection);
    void    performBlockShift(ShiftDirection direction);
    void    performAutoIndent();
    void    performTab();
    int     performTabReplacement(QTextCursor & cursor);
    void    handleCommenting();

private slots:
    void resetViewportMargins(int line_number_area_width, int minimap_width);
    void highlightCurrentLine();
    void updateLineNumberArea(const QRect &rect, int dy);
    void contentsChange_slot(int position, int charsRemoved, int charsAdded);
};

#endif // CodeEdit_H
